package cn.aoyu.test;

import cn.aoyu.test.bean.user.User;
import cn.aoyu.test.service.user.UserService;
import cn.aoyu.test.util.BaseConcurrentRequestUtil;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.w3c.dom.Element;

import java.lang.reflect.Method;

/**
 * @ClassName: SpringMybatisTestDemo
 * @Auther: xww
 * @Date: 2019/5/15 0015 14:19
 * @Description:
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-mybatis.xml")
public class SpringMybatisTestDemo extends BaseConcurrentRequestUtil {

	// applicationContext 解析
	private ApplicationContext context = new ClassPathXmlApplicationContext("spring-mybatis.xml");

	private UserService userService = (UserService) context.getBean("userServiceImpl");

	/**
	 * 测试数据库的锁机制 InnoDB （行锁、表锁）
	 */
	@Test
	public void dbInnoDBLock() throws InterruptedException {
		// 总的请求个数
		int requestTotal = 1000;
		// 同一时刻最大的并发线程的个数
		int concurrentThreadNum = 500;
		this.execute(requestTotal, concurrentThreadNum);
		System.out.println(atomicInteger);
	}

	/**
	 * 需要执行并发的方法
	 */
	@Override
	public void executeMethod() {
		userService.dbInnoDBLock();
	}

	/**
	 * applicationContext 解析
	 */
	@Test
	public void dbTest() {
		User user = userService.getUser(12);
		System.out.println(user.toString());
	}

	/**
	 * spring事务处理源码解析：
	 * Spring IOC原理源码解析(@Autowired原理详解 ：标识属性与方法)
	 * 学习网址：
	 * https://www.cnblogs.com/question-sky/p/6953315.html  （这一片文章写的比较好,将带有`@Component`、`@Service`、`@Controller`注解的类注册到spring的bean工厂中）
	 * https://blog.csdn.net/qq_27529917/article/details/78454912   (@Autowired原理解析：搜索每个Bean内@Autowired注解的信息)
	 *
	 * ================第一步:扫描包中类，注册到spring中===============
	 * new ClassPathXmlApplicationContext("spring-mybatis.xml")去解析spring-mybatis.xml中的标签
	 * <context:component-scan base-package="cn.aoyu.test"/>
	 * <context:annotation-config> 通过这些标签，这里会去调用下面这个类
	 * @see org.springframework.context.support.AbstractApplicationContext#refresh 方法中的obtainFreshBeanFactory()方法，如果xml中有自定义标签的话，
	 * 就会在这一步骤中进行自定义标签解析器的实例化(调用init()方法),比如
	 * @see org.springframework.context.config.ContextNamespaceHandler#init()
	 * 然后会去调用
	 * @see org.springframework.context.annotation.ComponentScanBeanDefinitionParser#parse(Element, ParserContext) 那么这个方法是在哪里被调用的呢？
	 * 解答：
	 * @see org.springframework.context.support.AbstractApplicationContext#refresh 方法中调用obtainFreshBeanFactory()方法，调用解析器中的parse(Element, ParserContext)方法
	 * @see org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
	 * 去调用delegate.parseCustomElement(root)  最终会调用
	 * @see org.springframework.beans.factory.xml.NamespaceHandlerSupport#parse(Element, ParserContext) 达到解析自定义标签的目的
	 * 去扫描包中使用了@component,@service,@Repository的类并注册成spring的bean
	 *
	 * ================第二步:refresh方法中去注册多个BeanPostProcessor接口实现类===============
	 * 在调用spring容器中的
	 * @see org.springframework.context.support.AbstractApplicationContext#refresh
	 * 方法中调用了的invokeBeanFactoryPostProcessors(beanFactory)去注册多个BeanPostProcessor接口实现类
	 * 供后续spring调用统一接口进行解析，比如下述的
	 *
	 * ConfigurationClassPostProcessor解析@Configuration注解类
	 * AutowiredAnnotationBeanPostProcessor解析@Autowired/@Value注解
	 * RequiredAnnotationBeanPostProcessor解析@Required注解
	 * CommonAnnotationBeanPostProcessor解析@Resource注解
	 * PersistenceAnnotationBeanPostProcessor解析JPA注解，持久层
	 *
	 * ================第三步:扫描每个bean中的有@Autowired注解的===============
	 * @see org.springframework.context.support.AbstractApplicationContext#refresh 方法中调用registerBeanPostProcessors(beanFactory) 然后去调用下面类中的方法
	 * @see org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean 里面会去调用initializeBean()方法，这里回去调用前置处理器和后置处理器
	 * 也会去调用下面的
	 * @see org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition
	 * 将扫描 Spring 容器中所有 Bean，当发现 Bean 中拥有@Autowired 注解时就找到和其匹配（默认按类型匹配）的 Bean，并注入到对应的地方中去
	 *
	 * ================@Transactional事务的jdk代理和调用逻辑===============
	 * <p>
	 * 使用service来调用mapper,调试数据库事务demo
	 *
	 * @see org.springframework.transaction.config.TxNamespaceHandler
	 * @see org.springframework.transaction.config.AnnotationDrivenBeanDefinitionParser#parse(Element, ParserContext)
	 * 这个方法最重要的是向Spring容器中注册了一个InfrastructureAdvisorAutoProxyCreator类，
	 * InfrastructureAdvisorAutoProxyCreator间接实现了BeanPostProcessor接口，
	 * 从AbstractAutoProxyCreator类中继承了postProcessAfterInitialization方法。
	 * Spring容器在初始化每个单例bean的时候，会遍历容器中的所有BeanPostProcessor实现类，
	 * 并执行其postProcessAfterInitialization方法。
	 * @see org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean 抽象类中调用了initializeBean方法是为了判断是否需要给类加代理
	 * @see org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization
	 * <p>
	 * 源码看到最后最终执行的是下面2个类中的方法
	 * 使用jdk代理 调用方法的时候会去调用下面的JdkDynamicAopProxy的invoke方法
	 * @see org.springframework.aop.framework.JdkDynamicAopProxy#invoke(Object, Method, Object[])
	 * JdkDynamicAopProxy中调用了this.advised.getInterceptorsAndDynamicInterceptionAdvice获取的是当前目标方法对应的拦截器，
	 * 里面是根据之前获取到的切面来获取相对应拦截器，这时候会得到TransactionInterceptor实例。
	 * 如果获取不到拦截器，则不会创建MethodInvocation，直接调用目标方法。
	 * 这里使用TransactionInterceptor创建一个ReflectiveMethodInvocation实例，
	 * 调用的时候进入ReflectiveMethodInvocation的proceed方法。
	 * @see org.springframework.transaction.interceptor.TransactionInterceptor#invoke
	 * @see org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction
	 */
	@Test
	public void transactionalMethodTest() {
		User userDetail = new User();
		userDetail.setAge(1);
		userDetail.setName("hj i love u");
		userDetail.setSex("0");
		userService.transactionalMethod(userDetail);
	}

}
